package com.lry.basic.jvm.asm.demo1;

import aj.org.objectweb.asm.*;

import javax.annotation.Resource;

import static aj.org.objectweb.asm.Opcodes.*;

public class HelloWorld {
//https://blog.51cto.com/lsieun/2924583
    //show bytecode
    public static byte[] genarate() {
        //将flags参数设置为ClassWriter.COMPUTE_MAXS或ClassWriter.COMPUTE_FRAMES，
        // 那么ASM会自动帮助我们计算Code结构中max_stack和max_locals的值。
        ClassWriter classWriter = new ClassWriter(0);
            // 定义对象头；版本号、修饰符、全类名、签名、父类、实现的接口
        classWriter.visit(V1_8, ACC_PUBLIC, "com/lry/HelloWorld", null, "java/lang/Object", null);
        // 添加方法；修饰符、方法名、描述符、签名、异常
//        MethodVisitor methodVisitor = classWriter.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
        MethodVisitor methodVisitor = classWriter.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "(Ljava/lang/String;)V", null, null);
        // 执行指令；获取静态属性
        methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
        // 加载常量 load constant
        methodVisitor.visitLdcInsn("Hello World");
        // 调用方法
        //虚方法，调用对象，调用方法，调用方法描述
        methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
        // 返回
        methodVisitor.visitInsn(RETURN);
        // 设置操作数栈的深度和局部变量的大小
        methodVisitor.visitMaxs(2, 1);
        // 方法结束
        methodVisitor.visitEnd();
        // 类完成
        classWriter.visitEnd();
        // 生成字节数组
        return classWriter.toByteArray();
    }

    public static byte[] g1(){
        //将flags参数设置为ClassWriter.COMPUTE_MAXS或ClassWriter.COMPUTE_FRAMES，
        // 那么ASM会自动帮助我们计算Code结构中max_stack和max_locals的值。
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
        cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, "com/lry/HelloWorld",
                null, "java/lang/Object", null);

        //属性
        //访问限定，属性名字，属性描述，泛型，属性值
        cw.visitField(ACC_PUBLIC|ACC_STATIC,"username","Ljava/lang/String;",null,"lry").visitEnd();
        cw.visitField(ACC_PUBLIC|ACC_STATIC,"age","I",null,25).visitEnd();
        //属性带注解
        FieldVisitor fv = cw.visitField(ACC_PUBLIC,"anno","Ljava/lang/String;",null,null);
        AnnotationVisitor annotationVisitor = fv.visitAnnotation("Ljavax/annotation/Resource;",false);
        annotationVisitor.visit("name","lry");
        fv.visitEnd();


        tConstruct(cw);
        tMethod(cw);
        tNew(cw);
        tIf(cw);
        tSwitch(cw);
        tFor(cw);
        tTry(cw);
        cw.visitEnd();

        return cw.toByteArray();
    }

    private static void tConstruct(ClassWriter cw) {
        //构造函数，方法名<init>
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
        mv.visitCode();
        mv.visitVarInsn(ALOAD, 0);
        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
        mv.visitInsn(RETURN);
        mv.visitMaxs(1, 1);
        mv.visitEnd();
    }

    private static void tMethod(ClassWriter cw) {
        //普通方法
        MethodVisitor methodVisitor = cw.visitMethod(ACC_PUBLIC , "hello", "()V", null, null);
        methodVisitor.visitCode();

        methodVisitor.visitInsn(ICONST_5);
        methodVisitor.visitVarInsn(ISTORE,1);
        methodVisitor.visitVarInsn(ILOAD,1);
        methodVisitor.visitInsn(ICONST_0);
        methodVisitor.visitInsn(IADD);
        methodVisitor.visitVarInsn(ISTORE,2);

        // 执行指令；获取静态属性
        methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
        // 加载常量 load constant
        methodVisitor.visitLdcInsn("Hello World");
        // 调用方法
        //虚方法，调用对象，调用方法，调用方法描述
        methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
        // 返回
        methodVisitor.visitInsn(RETURN);
        // 设置操作数栈的深度和局部变量的大小
        methodVisitor.visitMaxs(2, 1);
        // 方法结束
        methodVisitor.visitEnd();
    }


    //        NEW com/lry/basic/jvm/asm/demo1/GoodChild
//        DUP
//        LDC "Lucy"
//        BIPUSH 18
//        INVOKESPECIAL com/lry/basic/jvm/asm/demo1/GoodChild.<init> (Ljava/lang/String;I)V
//        ASTORE 1
//        ALOAD 1
//        INVOKEVIRTUAL com/lry/basic/jvm/asm/demo1/GoodChild.say ()V
//        RETURN
//        MAXSTACK = 4
//        MAXLOCALS = 2
    private static void tNew(ClassWriter cw) {
        MethodVisitor mv2 = cw.visitMethod(ACC_PUBLIC, "say", "()V", null, null);
        mv2.visitCode();
        mv2.visitTypeInsn(NEW, "com/lry/basic/jvm/asm/demo1/GoodChild");
        mv2.visitInsn(DUP);
        mv2.visitLdcInsn("Lucy");
        mv2.visitIntInsn(BIPUSH, 18);
        mv2.visitMethodInsn(INVOKESPECIAL, "com/lry/basic/jvm/asm/demo1/GoodChild", "<init>", "(Ljava/lang/String;I)V", false);
        mv2.visitVarInsn(ASTORE, 1);
        mv2.visitVarInsn(ALOAD,1);
        mv2.visitMethodInsn(INVOKEVIRTUAL,"com/lry/basic/jvm/asm/demo1/GoodChild","say","()V",false);
        mv2.visitInsn(RETURN);
        mv2.visitMaxs(4, 2);
        mv2.visitEnd();
    }


    //label使用 if

//    L0
//    ILOAD 1 加载方法局部变量表的1号位置元素，0是this
//    IFEQ L1 如果成立跳转到L1

//    L2 输出"value is true"
//    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
//    LDC "value is true"
//    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
//    GOTO L3 跳到L3

//    L1 输出"value is false"
//    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
//    LDC "value is false"
//    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V

//    L3
//    RETURN 方法返回

//    L4 栈深度和局部变量表元素个数
//    MAXSTACK = 2
//    MAXLOCALS = 2

    private static void tIf(ClassWriter cw) {
        //访问限定，名字，描述，泛型，异常
        MethodVisitor mv3 = cw.visitMethod(ACC_PUBLIC, "testIf", "(Z)V", null, null);
        Label elseLabel = new Label();      // 首先，准备两个Label对象
        Label returnLabel = new Label();

        //0x0000000: 1b99 000e b200 1912 2fb6 0021 a700 0bb2
        //    0x0000010: 0019 1231 b600 21b1

        mv3.visitCode();
        //0x15
        mv3.visitVarInsn(ILOAD, 1);
        //0x99
        mv3.visitJumpInsn(IFEQ, elseLabel);

        //0xb2
        mv3.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
        mv3.visitLdcInsn("value is true");
        //0xb6
        mv3.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
        //a7
        mv3.visitJumpInsn(GOTO, returnLabel);

        mv3.visitLabel(elseLabel);         // 将第一个Label放到这里
        //b2
        mv3.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
        mv3.visitLdcInsn("value is false");
        //b6
        mv3.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);

        mv3.visitLabel(returnLabel);      // 将第二个Label放到这里
        //b1
        mv3.visitInsn(RETURN);
        mv3.visitMaxs(2, 2);
        mv3.visitEnd();
    }

    private static void tSwitch(ClassWriter cw){
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC,"testSwitch","(I)V",null,null);
        Label l1 = new Label();
        Label l2 = new Label();
        Label defaultL = new Label();
        Label returnL = new Label();

        mv.visitCode();
        mv.visitVarInsn(ILOAD,1);
        //mv.visitTableSwitchInsn(1,2,defaultL,l1,l2); //1~2
        mv.visitLookupSwitchInsn(defaultL,new int[]{1,2},new Label[]{l1,l2});//1和2


        mv.visitLabel(l1);
        print(mv,"value = 1");
        mv.visitJumpInsn(GOTO, returnL);

        mv.visitLabel(l2);
        print(mv,"value = 2");
        mv.visitJumpInsn(GOTO, returnL);

        mv.visitLabel(defaultL);
        print(mv,"value = others");
        mv.visitJumpInsn(GOTO, returnL);

        mv.visitLabel(returnL);
        mv.visitInsn(RETURN);
        mv.visitMaxs(2,2);
        mv.visitEnd();
    }


    private static void print(MethodVisitor mv,String str){
        mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
        mv.visitLdcInsn(str);
        //0xb6
        mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
    }


    //    L0 变量0存到局部变量表1号位置
//    ICONST_0
//    ISTORE 1

//    L1 加载1号位，10入栈，比较出循环
//    ILOAD 1
//    BIPUSH 10
//    IF_ICMPGE L2

//    L3 打印一号位
//    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
//    ILOAD 1
//    INVOKEVIRTUAL java/io/PrintStream.println (I)V

//    L4 一号位自增
//    IINC 1 1
//    GOTO L1

//    L2
//    RETURN

//    L5
//    MAXSTACK = 2
//    MAXLOCALS = 2
//    public void testIf(){
//        for(int i=0;i<10;i++){
//            System.out.println(i);
//        }
//    }
    public static void tFor(ClassWriter cw){
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC,"testFor","()V",null,null);
        Label returnL = new Label();
        Label l1 = new Label();

        mv.visitCode();

        mv.visitInsn(ICONST_0);
        mv.visitVarInsn(ISTORE,1);

        //比较是否出循环
        mv.visitLabel(l1);
        mv.visitVarInsn(ILOAD,1);
        mv.visitIntInsn(BIPUSH,10);
        mv.visitJumpInsn(IF_ICMPGE,returnL);

        //打印
        mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
        mv.visitVarInsn(ILOAD,1);
        mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(I)V", false);

        //+1，跳转到l1
        mv.visitIincInsn(1,1);
        mv.visitJumpInsn(GOTO,l1);

        //返回
        mv.visitLabel(returnL);
        mv.visitInsn(RETURN);
        mv.visitMaxs(2,2);
        mv.visitEnd();
    }



    // TRYCATCHBLOCK L0 L1 L2 java/lang/InterruptedException
//    L0
//    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
//    LDC "Before Sleep"
//    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V

//    L3
//    LDC 1000
//    INVOKESTATIC java/lang/Thread.sleep (J)V
//
//    L4
//    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
//    LDC "After Sleep"
//    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
//
//    L1
//    GOTO L5
//
//    L2
//    ASTORE 1
//
//    L6
//    ALOAD 1
//    INVOKEVIRTUAL java/lang/InterruptedException.printStackTrace ()V
//
//    L5
//    RETURN
//
//    L7
//    MAXSTACK = 2
//    MAXLOCALS = 2

//    public void testTry(){
//        try {
//            System.out.println("Before Sleep");
//            Thread.sleep(1000);
//            System.out.println("After Sleep");
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
//    }


    private static void tTry(ClassWriter cw){
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC,"testTry","()V",null,null);

        Label returnL = new Label();
        Label l0 = new Label();
        Label l1 = new Label();
        Label l2 = new Label();

        mv.visitCode();

        mv.visitTryCatchBlock(l0,l1,l2,"java/lang/InterruptedException");

        mv.visitLabel(l0);
        print(mv,"Before Sleep");
        mv.visitLdcInsn(new Long(1000));
        mv.visitMethodInsn(INVOKESTATIC,"java/lang/Thread","sleep","(J)V",false);
        print(mv,"After Sleep");

        mv.visitLabel(l1);
        mv.visitJumpInsn(GOTO,returnL);

        mv.visitLabel(l2);
        mv.visitVarInsn(ASTORE,1);
        mv.visitVarInsn(ALOAD,1);
        mv.visitMethodInsn(INVOKEVIRTUAL,"java/lang/InterruptedException","printStackTrace","()V",false);

        mv.visitLabel(returnL);
        mv.visitInsn(RETURN);
        mv.visitMaxs(0,0);
        mv.visitEnd();
    }
}
